#include "tensor.h"
#include <QStringList>
#include <QVariant>
#include "vertex.h"
#include "halfedge.h"
#include "face.h"
#include "qmath.h"

#define TC "Tensor Curvature"
#define TT "TESTE"
#define T2 "TESTE2"

TensorPlugin::TensorPlugin()
{
}


QStringList TensorPlugin::perVertexPlugins()const
{
    QStringList st;
    st.append(QString(TC));
    return st;
}

QList< QPair<QString, DataType> > TensorPlugin::attributes(const QString& plugin) const
{
    QList< QPair<QString, DataType> > list;
    if(plugin == QString(TC) )
    {
        list.append( qMakePair(QString("null"), Vec3) );
        list.append( qMakePair(QString("diagTens"), Vec3) );
        list.append( qMakePair(QString("supTens"), Vec3) );
    }
    return list;
}

float TensorPlugin::signedAngle(const heds::HalfEdge* he)const
{
    if( he->face() == NULL || he->twin()->face() == NULL)
    {
        return M_PI;
    }
    QVector3D dest = he->getDestiny()->geometry().toVector3DAffine();
    QVector3D orin = he->origin()->geometry().toVector3DAffine();

    QVector3D edge =  (orin - dest);

    QVector3D v1 = he->twin()->face()->getNormal().normalized();
    QVector3D v2 = he->face()->getNormal().normalized();

    QVector3D base = QVector3D::crossProduct(v1,v2);

    float c = QVector3D::dotProduct(v1,v2);

    return qAcos(c)* ( QVector3D::dotProduct(edge,base) > 0.0 ? 1.0 : -1.0 );

}

float TensorPlugin::areaTri(const heds::HalfEdge* he)const
{
    QVector3D dest = he->getDestiny()->geometry().toVector3DAffine();
    QVector3D orin = he->origin()->geometry().toVector3DAffine();
    QVector3D v1 = (dest - orin);

    he = he->getPrev();

    dest = he->getDestiny()->geometry().toVector3DAffine();
    orin = he->origin()->geometry().toVector3DAffine();
    QVector3D v2 = (orin - dest);

    return QVector3D::crossProduct(v1,v2).length()/2.0;
}

QList<QVariant> TensorPlugin::attributeValue(const QString& plugin, int attIndex, const heds::Vertex *vertex)
{
    QList<QVariant> list;
    QVector3D ret;
    if(plugin == QString(TC))
    {

        //  _         _
        // | a   b   c |
        // | b   d   e |
        // |_c   e   f_|
        //

        if(attIndex == 0)
        {
            float area = 0.0;
            float a = 0.0, b = 0.0, c= 0.0, d=0.0, e = 0.0, f = 0.0;

            heds::Vertex::iterator it(vertex->begin());
            for(; it != vertex->end(); ++it)
            {
                QVector3D destG = (*it)->getDestiny()->geometry().toVector3DAffine();
                QVector3D origG = (*it)->origin()->geometry().toVector3DAffine();

                QVector3D edge = (destG - origG);

                float beta_e = signedAngle(*it);
                float norm_e = edge.length();
                float bc = beta_e*norm_e;
                edge.normalize();

                a += edge.x()*edge.x()*bc;
                b += edge.x()*edge.y()*bc;
                c += edge.x()*edge.z()*bc;
                d += edge.y()*edge.y()*bc;
                e += edge.y()*edge.z()*bc;
                f += edge.z()*edge.z()*bc;

                area += areaTri(*it);
            }

            area = 1.0/area;
            a *= area;
            b *= area;
            c *= area;
            d *= area;
            e *= area;
            f *= area;

            diag.insert(vertex, QVector3D(a,d,f));
            sup.insert(vertex, QVector3D(b,c,e));

            return list;
        }
        else if(attIndex == 1)
        {
            ret = diag[vertex];
        }else if(attIndex == 2)
        {
            ret = sup[vertex];
        }

        list.append(QVariant(ret.x()));
        list.append(QVariant(ret.y()));
        list.append(QVariant(ret.z()));

        return list;
    }
    return list;
}

void TensorPlugin::endOfVertices(const QString& plugin, int attIndex)
{
    if(plugin == QString(TC))
    {
        if(attIndex == 2)
        {
            diag.clear();
            sup.clear();
        }
    }
}

Q_EXPORT_PLUGIN2(CurvatureTensorPlugin, TensorPlugin)
